﻿using System;
using System.Linq;
using System.Collections;
using System.Collections.Generic;
using System.Text;
using System.Transactions;
using Newtonsoft.Json;
using T.ServiceComponent.Database.Service;
using T.FAS.Runtime.DataModel.Dao;
using T.FAS.Runtime.Metadata.Entity.DataModel;
using T.FAS.Runtime.Metadata.Entity;
using T.FAS.Runtime.Base.Entity;

namespace T.FAS.Runtime.DataModel.Dao
{
    /// <summary>
    /// 元数据验证接口
    /// </summary>
    public class CompositeDataObjectValidator : IMetaDataDesignValidator, IMetaDataRuntimeValidator
    {
        public const string MetaDataType = DataModelConst.CDOMetaType;
        public const string MetaDataTypeCN = "复合数据对象";
        /// <summary>
        /// 可修复的错误前缀
        /// </summary>
        public static readonly string NeedRepairCodePrefix = DataModelErrorCode.GetNeedRepairErrorCode(DataModelConst.CDOMetaType);

        //CDO依赖DO校验
        //private DatabaseValidator DatabaseValidator = new DatabaseValidator();
        private readonly DataObjectLogic _dataObjectELBDao = new DataObjectLogic();
        private readonly CompositeDataObjectLogic _compositeDataObjectLogic = new CompositeDataObjectLogic();

        public bool CreateMetadata(IMetaDataContent metaDataContent)
        {
            var Assert = GetEntityValidateResult(metaDataContent);
            Assert.FailIfErrorResult();
            return Assert.GetAssertResult().Result;
        }

        public bool DeleteMetadata(IMetaDataContent metaDataContent)
        {
            FASAssert Assert = new FASAssert();
            Assert.IsNotNull(metaDataContent.MetaDataID, $"{MetaDataTypeCN}删除时未指定MetaDataID");
            Assert.FailIfErrorResult();
            return Assert.GetAssertResult().Result;
        }

        public bool GetMetadata(IMetaDataContent metaDataContent)
        {
            var Assert = GetEntityValidateResult(metaDataContent, CRUDType.Retrive);
            Assert.FailIfErrorResult();
            return Assert.GetAssertResult().Result;
        }

        public bool UpdateMetadata(IMetaDataContent metaDataContent)
        {
            //因为目前更新采用先删后加的方式，所以采用相同校验规则
            return CreateMetadata(metaDataContent);
        }

        public bool GetMetadataRTP(IMetaDataContent metaDataContent)
        {
            //目前运行时获取元数据跟设计时一致
            return GetMetadata(metaDataContent);
        }

        /// <summary>
        /// 元数据修复
        /// </summary>
        /// <param name="metaDataContent"></param>
        public void Repair(IMetaDataContent metaDataContent)
        {
            object meta = metaDataContent;
            FASAssert fASAssert = new FASAssert();
            #region 注册校验信息
            //注册修复的校验方法
            fASAssert.Validator += delegate (ref object ValidateObj, object[] paramList)
            {
                return GetEntityValidateResult(ValidateObj as IMetaDataContent, CRUDType.Retrive, fASAssert)?.Result;
            };
            //注册回调
            fASAssert.FinishValidate += delegate (ref object ValidateObj)
            {
                _compositeDataObjectLogic.Update(metaDataContent as CompositeDataObject);
            };
            //注册修复方法
            fASAssert.RegistErrorHandler(DataModelValidateCodeConst.CDO.DeleteCurrentNodes, 
                (frame) =>
                    {
                        try
                        {
                            var cdo = frame.ValidateContext.ValidateObj as CompositeDataObject;
                            dynamic otherParams = frame.ValidateContext.OtherParams;
                            string deleteNodeID = otherParams.CurrentNodeID;
                            cdo.CompositeNodeObjects.RemoveAll(cno => cno.ID == deleteNodeID || cno.ParentObjectID == deleteNodeID);
                            return ErrorRepairResult.SuccessAndRetry;
                        }
                        catch (Exception e)
                        {
                            LocalLogService.WriteError(e);
                            return ErrorRepairResult.Error;
                        }
                    });
            fASAssert.RegistErrorHandler(DataModelValidateCodeConst.CDO.DeleteOuterAsso,
               (frame) =>
               {
                   try
                   {
                       var cdo = frame.ValidateContext.ValidateObj as CompositeDataObject;
                       dynamic otherParams = frame.ValidateContext.OtherParams;
                       string currentNodeID = otherParams.CurrentNodeID;
                       string outerAssoID = otherParams.OuterAssoID;
                       var cno = cdo.CompositeNodeObjects.FirstOrDefault(o => o.ID == currentNodeID);
                       var outerAsso = cno.Associations.FirstOrDefault(asso => asso.ID == outerAssoID);
                       var refEleIDs = outerAsso.RefElements.Select(item => item.TargetElementID);
                       //删除外键关联
                       cno.Associations.Remove(outerAsso);
                       //删除所有外键展示元素
                       cno.Elements.RemoveAll(ele => refEleIDs.Contains(ele.ID));
                       return ErrorRepairResult.SuccessAndRetry;
                   }
                   catch (Exception e)
                   {
                       LocalLogService.WriteError(e);
                       return ErrorRepairResult.Error;
                   }
               });
            fASAssert.RegistErrorHandler(DataModelValidateCodeConst.CDO.MatchElementAndDataColumnName,
               (frame) =>
               {
                   try
                   {
                       var cdo = frame.ValidateContext.ValidateObj as CompositeDataObject;
                       dynamic otherParams = frame.ValidateContext.OtherParams;
                       string currentNodeID = otherParams.CurrentNodeID;
                       List<string> notMatchElementIDs = otherParams.Elements;

                       var cno = cdo.CompositeNodeObjects.FirstOrDefault(o => o.ID == currentNodeID);
                       var notMatchElements = cno.Elements.Where(ele => notMatchElementIDs.Contains(ele.ID));
                       foreach (var ele in notMatchElements)
                       {
                           if (ele.DataColumn == null)
                               return ErrorRepairResult.Error;
                           ele.Alias = ele.Name = ele.DataColumn.ColumnName;
                       }
                       return ErrorRepairResult.SuccessAndRetry;
                   }
                   catch (Exception e)
                   {
                       LocalLogService.WriteError(e);
                       return ErrorRepairResult.Error;
                   }
               });
            #endregion
            //执行校验
            fASAssert.Validate(ref meta, 0, null);
        }

        private FASAssert GetEntityValidateResult(IMetaDataContent metaDataContent, CRUDType crudType = CRUDType.Create, FASAssert fASAssert = null)
        {
            FASAssert Assert;
            if (fASAssert != null)
                Assert = fASAssert;
            else
                Assert = new FASAssert();

            CompositeDataObject Meta = metaDataContent as CompositeDataObject;
            Assert.IsNotNull(Meta, $"请检查IMetaDataContent参数");
            Assert.FailIfErrorResult(); //如果为空引用直接抛出异常
            var unifyCode = $"元数据类型{MetaDataType}的元数据Code:{Meta.Code},ID:{Meta.ID}";

            /***********通用系统字段校验**********/
            StringBuilder errmsg = new StringBuilder();
            //MetaDataID,MetaDataCode,MetaDataName等字段校验
            var systemFieldsCheckResult = EntityCheckTools.CheckSystemFields(Meta);
            Assert.IsTrue(systemFieldsCheckResult.Result, systemFieldsCheckResult.ErrorMsg);

            if (crudType == CRUDType.Create)
            {
                var MetaDataInfo = metaDataContent.MetaData;
                Assert.IsNotNull(MetaDataInfo, $"{unifyCode}的系统字段:{nameof(metaDataContent.MetaData)}不能为空!", true);
                //租户ID验证
                Assert.IsNotNull(MetaDataInfo.TenantId, $"{unifyCode}的系统字段:{nameof(MetaDataInfo.TenantId)}不能为空!");
            }

            /*********************业务校验***********************/
            /*********************CDO校验***********************/
            //关闭CDO的【DatabaseID】字段必填限制
            //Assert.IsNotNull(Meta.DatabaseID, $"{unifyCode}的所属数据库ID{nameof(Meta.DatabaseID)}字段不能为空！");
            Assert.IsNotNull(Meta.RootCompositeNodeObjectID, $"{unifyCode}的根节点CNO.ID{nameof(Meta.RootCompositeNodeObjectID)}字段不能为空！");
            Assert.IsTrue(Meta.CompositeNodeObjects.Where(cno => cno.IsRootObject).Count() == 1, $"{unifyCode}的的主节点只能有一个", true);


            /*********************CNO校验***********************/
            Assert.IsTrue(Meta.CompositeNodeObjects != null && Meta.CompositeNodeObjects.Count > 0, $"{unifyCode}的CNO集合:{nameof(Meta.CompositeNodeObjects)}不能为空！");
            Assert.IsTrue(Meta.CompositeNodeObjects.All(cno => cno.CompositeDataObjectID == Meta.ID)); //运行时获取MetaDataID是否可能为空

            var counter = 0;
            foreach (var cno in Meta.CompositeNodeObjects)
            {
                if (crudType == CRUDType.Create)    //新增时从数据库取出DO实例，Get时已经赋值
                {
                    try
                    {
                        cno.DataObject = _dataObjectELBDao.GetDataObjectByID(cno.DataObjectID);
                    }
                    catch (Exception e)
                    {
                        throw new FasException(DataModelErrorCode.CDO.ValidError, e.Message, e);
                    }
                }
                Assert.IsNotNull(cno.DataObject, $"{unifyCode}的CNO节点:{cno.ID}的DO:{cno.DataObjectID}元数据实例不存在", true);
                Assert.IsTrue(cno.DataObject.IsStatic != true, $"{unifyCode}的主子节点树中的CNO节点:{cno.ID}的DO:{cno.DataObjectID}不能为SDO");
                cno.ChildCompositeNodeObjects = Meta.CompositeNodeObjects.Where(ccno => ccno.ParentObjectID == cno.ID).ToList();
                counter += cno.ChildCompositeNodeObjects.Count;
                 
            }
            Meta.RootCompositeNodeObject = Meta.CompositeNodeObjects.Find(o => o.IsRootObject);
            Assert.IsNotNull(Meta.RootCompositeNodeObject, $"{unifyCode}的主节点组装失败");

            Assert.IsTrue(counter + 1 == Meta.CompositeNodeObjects.Count, $"{unifyCode},构造CNO节点数失败，请检查各个节点ParentObjectID是否正确");

            /*********************CNO校验===CNO关系校验***********************/
            CDOCNO_AssoValidater(null, Meta.RootCompositeNodeObject, Assert, crudType, Meta);

            /**********获取时加入的额外控制***********/
            if (crudType == CRUDType.Retrive)
            {
                Assert.IsNotNull(Meta.ID, $"{unifyCode}的系统字段:{nameof(Meta.ID)}不能为空!");
                Assert.IsNotNull(Meta.Name, $"{unifyCode}的系统字段:{nameof(Meta.Name)}不能为空!");
                Assert.IsNotNull(Meta.Code, $"{unifyCode}的系统字段:{nameof(Meta.Code)}不能为空!");
            }

            return Assert;
        }

        /// <summary>
        /// CNO Asso有效性验证
        /// </summary>
        /// <param name="pNode"></param>
        /// <param name="node"></param>
        private void CDOCNO_AssoValidater(CompositeNodeObject pNode, CompositeNodeObject node, FASAssert assert, CRUDType crudType, CompositeDataObject cdo = null)
        {
            assert.IsNotNull(node, $"CNO不能为空!");
            //CNO节点验证
            CNOValidater(node, assert, crudType, cdo);
            var unifyCode = $"元数据类型{MetaDataType}的元数据Code:{cdo?.Code}的CNO节点:{node.ID}";

            #region 主子关联验证
            if (!node.IsRootObject)
            {
                assert.IsTrue(node.ParentObjectID == pNode.ID, $"{unifyCode}的PID:{node.ParentObjectID}与父节点ID:{pNode.ID}不匹配");
                var innerAssoList = node.Associations.FindAll(asso => asso.AssociateType == AssociateType.InnerJoin);
                assert.IsTrue(innerAssoList != null && innerAssoList.Count == 1, $"{unifyCode}的关联关系类型为InnerJoin的Asso只能有一个", true);
                var innerAsso = innerAssoList[0];
                // 跳过这里的验证，运行时CDO不需要这几个字段
                //Assert.IsTrue(innerAsso.CompositeDataObjectID == Node.CompositeDataObjectID, $"{unifyCode}主子关联Asso:{innerAsso.ID}中的CompositeDataObjectID必须与当前CDO.id一致");
                //Assert.IsTrue(innerAsso.CompositeNodeObjectID == Node.ID, $"{unifyCode}主子关联Asso:{innerAsso.ID}的CompositeNodeObjectID必须是当前节点ID");
                assert.IsTrue(innerAsso.AssoCompositeDataObjectID == node.CompositeDataObjectID, $"{unifyCode}主子关联Asso:{innerAsso.ID}中的AssoCompositeDataObjectID必须与当前CDO.id一致");
                assert.IsTrue(innerAsso.AssoCompositeNodeObjectID == pNode.ID, $"{unifyCode}主子关联的Asso:{innerAsso.ID}的CompositeNodeObjectID必须是父节点ID");
                var innerAssoItemList = innerAsso.Items;
                assert.IsTrue(innerAssoItemList != null && innerAssoItemList.Count == 1, $"{unifyCode}的Asso:{innerAsso.ID}中关联关系类型为InnerJoin的AssoItem有且只能有一个", true);
                var innerAssoItem = innerAssoItemList[0];
                var innerItemSourceEle = node.Elements.Find(ele => ele.ID == innerAssoItem.SourceElementID);
                var innerItemTargetEle = pNode.Elements.Find(ele => ele.ID == innerAssoItem.TargetElementID);
                var innerItemSourceEleMappingColumn = node.DataObject.Columns.Find(col => col.ID == innerItemSourceEle.DataColumnID);
                var innerItemTargetEleMappingColumn = pNode.DataObject.Columns.Find(col => col.ID == innerItemTargetEle.DataColumnID);
                //很可能是子级DO修改了数据类型，不再关联主DO，处理方式是将当前节点，以及其子节点全都去掉
                AssertErrorFrame assertErrorFrame = assert.CreateFrame(DataModelValidateCodeConst.CDO.DeleteCurrentNodes);
                assertErrorFrame.ValidateContext.OtherParams = new
                {
                    CurrentNodeID = node.ID
                };

                assert.IsTrue(innerItemSourceEleMappingColumn.IsFkColumn, $"{DataModelErrorCode.GetNeedRepairErrorCode(DataModelConst.CDOMetaType)},{unifyCode}的DO:{node.DataObject.Code}的外键列{innerItemSourceEleMappingColumn.ColumnName}的IsFkColumn必须为true", true, assertErrorFrame);
                
                assert.IsTrue(innerItemSourceEleMappingColumn.RelatedDataObjectID == pNode.DataObject.ID, $"{DataModelErrorCode.GetNeedRepairErrorCode(DataModelConst.CDOMetaType)},{unifyCode}的关联DO:{node.DataObject.Code}的外键列:{innerItemSourceEleMappingColumn.ID}RelatedDataObjectID必须与父节点关联的DO.ID相同", true, assertErrorFrame);
            }
            #endregion

            #region 外键关联验证
            var outerAssoList = node.Associations.FindAll(asso => asso.AssociateType == AssociateType.OuterLeftJoin);
            if (outerAssoList.Count > 0)
            {
                foreach (var outerAsso in outerAssoList)
                {
                    DataObject outerAssoDataObject;
                    //验证关联DO有效性
                    if (crudType == CRUDType.Create)
                    {
                        try
                        {
                            outerAssoDataObject = _dataObjectELBDao.GetDataObjectByID(outerAsso.AssoDataObjectID);
                        }
                        catch (Exception e)
                        {
                            throw new FasException(DataModelErrorCode.CDO.ValidError, e.Message, e);
                        }
                    }
                    else
                    {
                        outerAssoDataObject = outerAsso.AssoDataObject;
                    }
                    //构造外键问题的错误上下文，用于同一处理由于外键关联错误导致的报错。统一处理方案为删掉外键关联Asso，以及外键展示字段Ref
                    AssertErrorFrame assertErrorFrame = assert.CreateFrame(DataModelValidateCodeConst.CDO.DeleteOuterAsso);
                    assertErrorFrame.ValidateContext.OtherParams = new
                    {
                        CurrentNodeID = node.ID,
                        OuterAssoID = outerAsso.ID
                    };


                    assert.IsNotNull(outerAssoDataObject
                        , $"{NeedRepairCodePrefix},{unifyCode}的外键关联关系Asso:{outerAsso.ID}的关联DO:{outerAsso.AssoDataObjectID}获取DO实例失败", true, assertErrorFrame);
                    assert.IsTrue(outerAssoDataObject != null && outerAssoDataObject.ID == outerAsso.AssoDataObjectID
                        , $"{NeedRepairCodePrefix},{unifyCode}的外键关联关系Asso:{outerAsso.ID}的外键关联DO:{outerAsso.AssoDataObjectID}不存在！", true, assertErrorFrame);
                    // 跳过这里的验证，运行时CDO不需要这几个字段
                    //Assert.IsTrue(outerAsso.CompositeDataObjectID == Node.CompositeDataObjectID
                    //    , $"{NeedRepairCodePrefix},{unifyCode}的外键关联关系Asso中的CompositeDataObjectID必须与当前CDO.id一致", true, assertErrorFrame);
                    //Assert.IsTrue(outerAsso.CompositeNodeObjectID == Node.ID
                    //    , $"{NeedRepairCodePrefix},{unifyCode}的外键关联关系Asso的CompositeNodeObjectID必须是当前节点ID", true, assertErrorFrame);

                    //外键关联Asso Item
                    var outerAssoItemList = outerAsso.Items;
                    assert.IsTrue(outerAssoItemList != null && outerAssoItemList.Count == 1, "关联关系类型为OuterJoin的AssoItem只能有一个", true);
                    var outerAssoItem = outerAssoItemList[0];
                    var outerItemSourceEle = node.Elements.Find(ele => ele.ID == outerAssoItem.SourceElementID);
                    assert.IsNotNull(outerItemSourceEle
                        , $"{NeedRepairCodePrefix},{unifyCode}的外键关联关系Asso:{outerAsso.ID}的外键关联Item:{outerAssoItem.ID}的SourceElementID:{outerAssoItem.SourceElementID}未在当前节点找到对应的Element", true, assertErrorFrame);

                    var outerItemSourceEleMappingColumn = node.DataObject.Columns.Find(col => col.ID == outerItemSourceEle.DataColumnID);
                    assert.IsNotNull(outerItemSourceEleMappingColumn
                        , $"{NeedRepairCodePrefix},{unifyCode}的外键关联关系Asso:{outerAsso.ID}的外键关联Item:{outerAssoItem.ID}的SourceElementID:{outerAssoItem.SourceElementID}对应的DataColumn未找到", true, assertErrorFrame);

                    
                    //很可能是引用当前节点DO的DO修改了数据类型，不再关联当前DO，处理方式是将外键关联去掉
                    assert.IsTrue(outerAsso.AssoDataObjectID == outerItemSourceEleMappingColumn.RelatedDataObjectID
                                , $"{NeedRepairCodePrefix},{unifyCode}的外键关联关系Asso:{outerAsso.ID}的外键关联数据对象ID:{outerAsso.AssoDataObjectID}" +
                                  $"必须跟对应的当前CNO节点关联的数据对象Code:{node.DataObject.Code}的列:{outerItemSourceEleMappingColumn.ColumnName}" +
                                  $"关联的数据对象RelatedDataObjectID:{outerItemSourceEleMappingColumn.RelatedDataObjectID}匹配", true, assertErrorFrame);
                    assert.IsTrue(outerAssoDataObject.Columns.Exists(col => col.ID == outerAssoItem.TargetElementID),
                                  $"{NeedRepairCodePrefix},{unifyCode}的外键关联Asso:{outerAsso.ID}的" +
                                  $"AssoItem:{outerAssoItem.ID}的" +
                                  $"TargetElementID:{outerAssoItem.TargetElementID}" +
                                  $"不存在与外键关联DO:{outerAssoDataObject.Code}的列中", true, assertErrorFrame);

                    //外键关联Asso Ref
                    var outerAssoRefList = outerAsso.RefElements;
                    //是否必须有一个Refelemenet
                    assert.IsTrue(outerAssoRefList != null && outerAssoRefList.Count > 0, $"{NeedRepairCodePrefix},{unifyCode}的外键关联Asso:{outerAsso.ID}必须设置至少一个RefElements关联", true, assertErrorFrame);
                    foreach (var refele in outerAssoRefList)
                    {
                        assert.IsTrue(outerAsso.AssoDataObjectID == refele.DataObjectID, $"{NeedRepairCodePrefix},{unifyCode}的外键关联Asso:{outerAsso.ID}的refele:{refele.ID}的DO.id:{refele.DataObjectID}与Asso中:{outerAsso.AssoDataObjectID}不一致", true, assertErrorFrame);
                        assert.IsTrue(outerAssoDataObject.Columns.Exists(col => col.ID == refele.SourceElementID), $"{NeedRepairCodePrefix},{unifyCode}的外键关联Asso:{outerAsso.ID}的外键关联DO:{outerAssoDataObject.Code}不存在id为{refele.SourceElementID}的列", true, assertErrorFrame);
                        assert.IsTrue(node.Elements.Exists(ele => ele.ID == refele.TargetElementID && ele.ElementType == ElementType.Reference), $"{NeedRepairCodePrefix},{unifyCode}的外键关联Asso:{outerAsso.ID}的refele:{refele.ID}的目标reference ele在当前节点中不存在", true, assertErrorFrame);
                    }
                }
            }
            #endregion

            foreach (var child in node.ChildCompositeNodeObjects)
            {
                CDOCNO_AssoValidater(node, child, assert, crudType, cdo);
            }
        }

        /// <summary>
        /// CNO验证有效性
        /// </summary>
        /// <param name="node"></param>
        private void CNOValidater(CompositeNodeObject node, FASAssert assert, CRUDType crudType, CompositeDataObject cdo = null)
        {
            var unifyCode = $"元数据类型{MetaDataType}的元数据Code:{cdo?.Code}的CNO节点:{node.ID}";
            assert.IsNotNull(node.CompositeDataObjectID, $"{unifyCode}的{nameof(node.CompositeDataObjectID)}字段不能为空");
            if (!node.IsRootObject)
            {
                assert.IsNotNull(node.ParentObjectID, $"{unifyCode}的{nameof(node.ParentObjectID)}字段不能为空");
            }
            if (crudType == CRUDType.Retrive)
            {
                assert.IsNotNull(node.DataObjectTableAlias, $"{unifyCode}的{nameof(node.DataObjectTableAlias)}字段不能为空");
                if (!node.IsRootObject)
                {
                    assert.IsNotNull(node.ParentObject, $"{unifyCode}的{nameof(node.ParentObject)}实体不能为空");
                }
                foreach (var ele in node.Elements)
                {
                    if (ele.ElementType == ElementType.Normal)   //初始化所有Normal Element对应的DataColumn对象
                    {
                        ele.DataColumn = node.DataObject?.Columns.Find(col => col.ID == ele.DataColumnID);
                        if (ele.DataColumn == null)
                            throw new FasException(DataModelErrorCode.CDO.InitError, $"{DataModelErrorCode.GetNeedRepairErrorCode(DataModelConst.CDOMetaType)},{unifyCode}的元素Element:{ele.ID}对应的DO:{node.DataObject?.Code}对应的列名:{ele.Alias ?? ele.Name}, ID:{ele.DataColumnID}不存在!");
                    }
                }
                //DO列名与Element名称匹配检测
                var normalElements = node.Elements.Where(ele => ele.ElementType == ElementType.Normal).ToList();
                List<string> NotMatchingElements = new List<string>();
                foreach (var ele in normalElements)
                {
                    if ((ele.Alias ?? ele.Name) != ele.DataColumn.ColumnName)
                    {
                        NotMatchingElements.Add(ele.ID);
                    }
                }
                AssertErrorFrame assertErrorFrame = assert.CreateFrame(DataModelValidateCodeConst.CDO.MatchElementAndDataColumnName);
                assertErrorFrame.ValidateContext.OtherParams = new
                {
                    CurrentNodeID = node.ID,
                    Elements = NotMatchingElements
                };
                assert.IsTrue(NotMatchingElements.Count == 0, 
                    $"{DataModelErrorCode.GetNeedRepairErrorCode(DataModelConst.CDOMetaType)}," +
                    $"{unifyCode}存在名称与对应的DataObject:{node.DataObject?.Code}的列名不匹配的元素, " +
                    $"数量为:{NotMatchingElements.Count}", true, assertErrorFrame);
            }
           

            //普通列数量，关闭DO列跟CNO.Ele匹配的验证
            return;
            var eleCount = node.Elements.Where(ele => ele.ElementType == ElementType.Normal).Count();
            assert.IsTrue(eleCount == node.DataObject.Columns.Count, 
                $"CDO:{node.CompositeDataObjectID}的CNO节点:{node.ID}的Element数量{eleCount}与DO:{node.DataObject.Code}的列数量{node.DataObject.Columns.Count}不相等，是否需要同步或者关闭Element数量与Columns匹配验证");
            assert.IsTrue(node.Elements.Where(ele => ele.ElementType == ElementType.Normal).All(ele => node.DataObject.Columns.Find(col => col.ID == ele.DataColumnID) != null),
                $"CDO:{node.CompositeDataObjectID}的CNO节点:{node.ID}的Elements元素信息必须跟对应的DataObject:{node.DataObject.Code},{node.DataObjectID}的列匹配");
        }
    }
}
